package project;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

public class Main {
	
	static List<Move> movesDone = new ArrayList<Move>();
	static Stack<Move> movesUndone = new Stack<Move>();
	static Stack<GameState> done = new Stack<GameState>();
	static Stack<GameState> undone = new Stack<GameState>();
	
	static int ai = Player.MINIMAX;
	static int aiPlayers = 0;
	
	static Player[] players = new Player[2];
	
	public static void main (String[] args) throws IOException {
		
		Minimax.setDepth(2);
		Minimax.setTimeout(2000);
		
		Input input;
		
		do {
			input = new Input();
			executeCommand(input);
		} while (!(input.command == Command.EXIT));
		
	}
	
	private static void executeCommand(Input input) throws IOException {
		if (input.isValid()) {
			
			switch (input.command) {
			
			case NEW:
				clearLists();
				aiPlayers = new Integer(input.required);
				for (int a = 0; a < 2; ++a) {
					if (a < aiPlayers) {
						players[a] = Player.newPlayer(ai, a);
					} else {
						players[a] = new HumanPlayer(a);
					}
				}
				loadMoves(input.optional.toArray());
				while (!players[done.peek().currPlayer].isHuman() && done.peek().gameOver() == GameState.GAMENOTOVER) {
					System.out.println("querying AI");
					GameState next = done.peek().clone();
					Move nextMove = players[done.peek().currPlayer].getNextMove(next);
					System.out.println(nextMove);
					next.applyMove(nextMove);
					movesDone.add(nextMove);
					done.push(next);
					System.out.print(done.peek());
				}
			break;
			case SETAI:
				setAiAttributes(input.required);
				for (String next : input.getOptional()) {
					Pattern pat = Pattern.compile("^-d(.*)$");
					if (pat.matcher(next).matches()) {
						Minimax.setDepth(new Integer(next.substring(2)));
					}
					pat = Pattern.compile("^-t(.*)$");
					if (pat.matcher(next).matches()) {
						Minimax.setTimeout(new Integer(next.substring(2)));
					}
				}
			break;
			case LOAD:
				clearLists();
				
				File file = new File("saves/"+input.required);
				if (file.exists()) {
					BufferedReader bf = new BufferedReader(new FileReader(file));
					aiPlayers = new Integer(bf.readLine());
					if (aiPlayers > 0) {
						setAiAttributes(bf.readLine());
						Minimax.setDepth(new Integer(bf.readLine()));
						Minimax.setTimeout(new Integer(bf.readLine()));
					} else {
						setAiAttributes("");
					}
					String moves = bf.readLine();
					loadMoves(moves.split("\\s+"));
					bf.close();
				}
			break;
			case SAVE:
				new File("saves").mkdir();
				File f = new File("saves/"+input.required);
				f.createNewFile();
				
				StringBuilder sb = new StringBuilder();
				sb.append(aiPlayers+"\n");
				if (aiPlayers > 0) {
					sb.append(ai+"\n");
					sb.append(Minimax.getMaxDepth()+"\n");
					sb.append(Minimax.getTimeout()+"\n");
				}
				for (int i = 0; i < movesDone.size(); ++i) {
					if (i > 0) {
						sb.append(" ");
					}
					sb.append(movesDone.get(i));
				}
				FileWriter fw = new FileWriter(f);
				fw.write(sb.toString());
				fw.close();
				
				System.out.println("Game saved as "+input.required);
			break;
			case MOVE:
				System.out.println("move: "+input.required);
				Move move = Move.newMove(input.required, done.peek().currPlayer);
				GameState next;
				if (done.size() != 0)
					next = done.peek().clone();
				else
					next = new GameState();
				if (!Validity.isValid(move, next)) {
					System.out.println("This move is invalid");
				} else {
					next.applyMove(move);
					movesDone.add(move);
					movesUndone.clear();
					done.push(next);
					undone.clear();
				}
				System.out.print(done.peek());
				if (!players[done.peek().currPlayer].isHuman() && done.peek().gameOver() == GameState.GAMENOTOVER) {
					System.out.println("querying AI");
					next = done.peek().clone();
					Move nextMove = players[done.peek().currPlayer].getNextMove(next);
					System.out.println(nextMove);
					next.applyMove(nextMove);
					movesDone.add(nextMove);
					done.push(next);
					System.out.print(done.peek());
				}
			break;
			case UNDO:
				if (movesDone.isEmpty()) {
					System.out.println("There are no moves to undo");
				} else {
					undone.push(done.pop());
					movesUndone.push(movesDone.remove(movesDone.size()-1));
					if (!players[done.peek().currPlayer].isHuman()) {
						undone.push(done.pop());
						movesUndone.push(movesDone.remove(movesDone.size()-1));
					}
					System.out.print(done.peek());
				}
			break;
			case REDO:
				if (undone.isEmpty()) {
					System.out.println("There are no moves to redo");
				} else {
					if (!players[undone.peek().currPlayer].isHuman()) {
						done.push(undone.pop());
						movesDone.add(movesUndone.pop());
					}
					done.push(undone.pop());
					movesDone.add(movesUndone.pop());
					System.out.print(done.peek());
				}
			break;
			case VALID:
				String required = input.required;
				if (!done.isEmpty()) {
					List<Move> validmoves = done.peek().validMoves();
					if (required.equals("all")) {
						if (input.getOptional().isEmpty()) {
							convertListToTable(validmoves, "all");
						} else {
							//Iterator iter1 = input.getOptional().iterator();
							//while (iter1.hasNext()) {
							for (String next1 : input.getOptional()) {
								//String next1 = (String) iter1.next();
								Pattern pat = Pattern.compile("^-w$");
								if (pat.matcher(next1).matches()) {
									convertListToTable(validmoves, "-w");
								}
								pat = Pattern.compile("^-p$");
								if (pat.matcher(next1).matches()) {
									convertListToTable(validmoves, "-p");				
								}
							}
						}
					} else {
						System.out.println(Validity.isValid(Move.newMove(required, done.peek().currPlayer), done.peek()));
					}
					//System.out.println("validmoves: "+validmoves.toString().replace(",","\n"));
				break;
				}
			}
		}
	}
	
	private static void loadMoves(Object[] moves) {
		done.add(new GameState());
		for (int a = 0; a < moves.length; ++a) {
			GameState clone = done.peek().clone();
			System.out.println(moves[a]);
			Move nextMove = Move.newMove(moves[a].toString(), clone.currPlayer);
			if (Validity.isValid(nextMove, clone)) {
				movesDone.add(nextMove);
				done.push(clone.applyMove(movesDone.get(movesDone.size()-1)));
			} else {
				System.out.println("Invalid move included.  Performed all valid moves");
				break;
			}
		}
		System.out.print(done.peek());
	}
	
	@SuppressWarnings("static-access")
	private static void convertListToTable(List<? extends Object> list, String type) {
		int j = 0;
		System.out.println("displaying "+type+" valid moves:");
		Pattern pattern = Pattern.compile("^-w$");
		for (int i = 0; i < list.size(); ++i) {
			if (j < 15) {
				Object nextElement = list.get(i);
				if (type.equals("all")) {
					System.out.printf("%3s | ", nextElement);
					if (j == 14) {
						j = 0;
						System.out.println("");
					}
				} else {
					if (type.equals("-p")) {
						if (pattern.matches("[a-i][1-9]", nextElement.toString())) {
							System.out.printf("%3s | ", nextElement);
							if (j == 14) {
								j = 0;
								System.out.println("");
							}
						}
					} else if (type.equals("-w")) {
						if (pattern.matches("[a-i][1-9](h|v)", nextElement.toString())) {
							System.out.printf("%3s | ", nextElement);
							if (j == 14) {
								j = 0;
								System.out.println("");
							}
						}
					}
				}
				j++;
			}
		}
		System.out.println("");
	}
	
	private static void setAiAttributes(String input) {
		System.out.println("input: "+input);
		if (input.equals("minimax")) {
			ai = Player.MINIMAX;
		} else if (input.equals("sensible")) {
			ai = Player.SENSIBLE;
		} else if (input.equals("random")) {
			ai = Player.RANDOM;
		}
		for (int a = 0; a < 2; ++a) {
			if (a < aiPlayers) {
				players[a] = Player.newPlayer(ai, a);
			} else {
				players[a] = new HumanPlayer(a);
			}
		}
	}
	
	private static void clearLists() {
		movesDone.clear();
		movesUndone.clear();
		done.clear();
		undone.clear();
	}
}